﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using JMT.Controls.Relations;
using JMT.Controls.Elements;
using JMT.Data;
using System.IO;

namespace JMT.Controls
{
    /// <summary>
    /// ClassDiagram.xaml 的交互逻辑
    /// </summary>
    public partial class ClassDiagram : UserControl
    {
        public Data.JSolution Solution { get; private set; }
        public Data.JDiagram JDiagram { get; set; }

        DiagramMode _mode;
        private string _linkType { get; set; }

        /// <summary>
        /// 拖动信息元素
        /// </summary>
        private DragInfo _dragInfo = new DragInfo();

        /// <summary>
        /// 设置连接时候的预览线
        /// </summary>
        private Line _previewLink;

        private List<Elements.IJBox> _elements = new List<Elements.IJBox>();

        private List<Relations.JLink> _links = new List<Relations.JLink>();

        private Elements.IJBox _activeElement = null;

        public ClassDiagram(Data.JSolution solution, Data.JDiagram diagramModel)
        {
            InitializeComponent();

            this.AllowDrop = true;

            Solution = solution;
            JDiagram = diagramModel;

            _ctlCanvas.MouseMove += new MouseEventHandler(_ctlCanvas_MouseMove);
            _ctlCanvas.MouseLeftButtonDown += new MouseButtonEventHandler(_ctlCanvas_MouseLeftButtonDown);
            _ctlCanvas.MouseLeftButtonUp += new MouseButtonEventHandler(_ctlCanvas_MouseLeftButtonUp);
            _ctlCanvas.MouseRightButtonDown += new MouseButtonEventHandler(_ctlCanvas_MouseRightButtonDown);

            List<JRelation> visibleRelation = new List<JRelation>();
            List<JElement> elementList = diagramModel.GetChildren<JElement>();
            List<JRelation> relationList = JData.Current.Solution.GetChildren<JRelation>();
            foreach (var jElement in elementList)
            {
                Data.JModel jModel = JData.Current.Solution.GetModel(jElement.ModelId);
                Elements.IJBox panel = JBoxFactory.Create(jModel, jElement);

                // 注册到事件总线上
                JEventBus.GetInstance().Register("JMT.MODEL.ADD", (IEventHandler)panel);

                this._ctlCanvas.Children.Add((UIElement)panel);
                panel.SetPos(new Point(jElement.Left, jElement.Top));
                _elements.Add(panel);
                panel.Diagram = this;

                // 检查是否有关联关系
                foreach (var jRelation in relationList)
                {
                    bool isVisible = false;
                    foreach (var jE in elementList)
                    {
                        if (jE.ModelId == jRelation.ToId)
                        {
                            isVisible = true;
                            break;
                        }
                    }
                    // 创建连接对象
                    if (isVisible && !visibleRelation.Contains(jRelation))
                    {
                        visibleRelation.Add(jRelation);
                    }
                }

            }

            foreach (var jRelation in visibleRelation)
            {
                IJBox fromBox = null;
                IJBox toBox = null;
                foreach (var box in _elements)
                {
                    if (jRelation.FromId == box.ElementModel.ModelId)
                    {
                        fromBox = box;
                    }

                    if (jRelation.ToId == box.ElementModel.ModelId)
                    {
                        toBox = box;
                    }

                    if (fromBox != null && toBox != null)
                    {
                        break;
                    }
                }

                JLink link = JLinkFactory.Create(jRelation.Type);
                link.Attach(fromBox, toBox);
                link.Relation = jRelation;
                this._links.Add(link);
                this._ctlCanvas.Children.Add((UIElement)link);
            }

            _mode = DiagramMode.Render;
        }

        #region 鼠标事件
        void _ctlCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point pos = e.GetPosition(_ctlCanvas);

            _activeElement = null;

            if (_mode == DiagramMode.Link)
            {
                foreach (var element in _elements)
                {
                    Rect rect = element.GetRect();
                    if (rect.Left <= pos.X && rect.Top <= pos.Y && rect.Right >= pos.X && rect.Bottom >= pos.Y)
                    {
                        _activeElement = element;
                        break;
                    }
                }
                // 添加连接预览线
                _previewLink = new Line();
                this._ctlCanvas.Children.Add(_previewLink);
                _previewLink.X1 = pos.X;
                _previewLink.Y1 = pos.Y;
                _previewLink.StrokeThickness = 1;
                _previewLink.Stroke = Brushes.Black;
                _previewLink.Visibility = System.Windows.Visibility.Hidden;
            }
            else
            {
                foreach (var element in _elements)
                {
                    Rect rect = element.GetRect();
                    if (rect.Left <= pos.X && rect.Top <= pos.Y && rect.Right >= pos.X && rect.Bottom >= pos.Y)
                    {
                        _activeElement = element;
                        _dragInfo.IsDragging = true;
                        _dragInfo.Offset = new Point(pos.X - rect.Left, pos.Y - rect.Top);
                        break;
                    }
                }
            }
        }

        void _ctlCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Point pos = e.GetPosition(_ctlCanvas);

            if (_mode == DiagramMode.Link)
            {
                IJBox activeElement = null;
                foreach (var element in _elements)
                {
                    Rect rect = element.GetRect();
                    if (rect.Left <= pos.X && rect.Top <= pos.Y && rect.Right >= pos.X && rect.Bottom >= pos.Y)
                    {
                        activeElement = element;
                        break;
                    }
                }

                if (activeElement != null)
                {
                    this._ctlCanvas.Children.Remove(_previewLink);
                    _previewLink = null;

                    // relation 和 refer需要配对添加
                    JRelation jRelation = (JRelation)JModelFactory.Create(_linkType);//JData.Current.AddModel<JRelation>(JData.Current.Solution);
                    JData.Current.AddModel(JData.Current.Solution, jRelation);
                    jRelation.Type = _linkType;
                    jRelation.FromId = _activeElement.ClassModel.Id;
                    jRelation.ToId = activeElement.ClassModel.Id;
                    
                    //jContainer.Relations.Add(jRelation);

                    /*
                    JRefer jRefer = new JRefer();
                    jRefer.Id = Guid.NewGuid();
                    jRefer.Name = "refer";
                    jRefer.FromModel = _activeElement.ClassModel;
                    jReferContainer.ReferCollection.Add(jRefer);
                    */

                    JLink link = JLinkFactory.Create(_linkType);
                    link.Attach(_activeElement, activeElement);
                    link.Relation = jRelation;
                    this._links.Add(link);
                    this._ctlCanvas.Children.Add((UIElement)link);
                }

                EndLink();
            }
            else
            {
                _dragInfo.IsDragging = false;
            }
        }

        /// <summary>
        /// 右键点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _ctlCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Elements.IJBox firedElement = null;

            Point pos = e.GetPosition(_ctlCanvas);
            foreach (var element in _elements)
            {
                Rect rect = element.GetRect();
                if (rect.Left <= pos.X && rect.Top <= pos.Y && rect.Right >= pos.X && rect.Bottom >= pos.Y)
                {
                    firedElement = element;
                    break;
                }
            }

            if (firedElement != null)
            {
                //_classElementCXTMenu.Tag = firedElement;
                //_classElementCXTMenu.IsOpen = true;
                ContextMenu menu = ContextMenus.ContextMenuFactory.Get(firedElement.ClassModel.Type + "box");
                menu.Tag = firedElement;
                menu.DataContext = firedElement.ClassModel;
                menu.IsOpen = true;
                //MessageBox.Show("right mouse click on element");
            }
            else
            {
                //_cxtMenuDiagram.IsOpen = true;
                ContextMenu menu = ContextMenus.ContextMenuFactory.Get("diagram");
                menu.Tag = this;
                menu.DataContext = this.Solution;
                menu.IsOpen = true;
                //MessageBox.Show("right mouse click on diagram");
            }
        }

        void _ctlCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point pos = e.GetPosition(_ctlCanvas);

            if (_mode == DiagramMode.Link && _previewLink != null)
            {
                _previewLink.X2 = pos.X;
                _previewLink.Y2 = pos.Y;
                _previewLink.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                if (_dragInfo.IsDragging && _activeElement != null)
                {
                    double newTop = pos.Y - _dragInfo.Offset.Y;
                    double newLeft = pos.X - _dragInfo.Offset.X;
                    _activeElement.ElementModel.Left = newLeft;
                    _activeElement.ElementModel.Top = newTop;

                    _activeElement.SetPos(new Point(newLeft, newTop));
                }
            }
        }
        #endregion

        public void AddElement(Data.JModel jmClass, double posX = 10, double posY = 10)
        {
            List<JElement> elementList = JDiagram.GetChildren<JElement>();
            foreach (var item in elementList)
            {
                if (item.ModelId == jmClass.Id)
                {
                    return;
                }
            }

            Data.JElement jmElement = JData.Current.AddModel<JElement>(JDiagram);
            jmElement.ModelId = jmClass.Id;
            jmElement.Left = posX;
            jmElement.Top = posY;

            IJBox box = JBoxFactory.Create(jmClass, jmElement);
            // 注册到事件总线上
            JEventBus.GetInstance().Register("JMT.MODEL.ADD", (IEventHandler)box);

            UIElement element = (UIElement)box;
            this._ctlCanvas.Children.Add(element);
            Canvas.SetLeft(element, jmElement.Left);
            Canvas.SetTop(element, jmElement.Top);
            _elements.Add(box);
            box.Diagram = this;
        }

        public void DeleteElement(Data.JModel jModel)
        {
            foreach (var jBox in _elements)
            {
                if (jBox.ElementModel.ModelId == jModel.Id)
                {
                    this._ctlCanvas.Children.Remove((UIElement)jBox);
                    break;
                }
            }

            List<JElement> elementList = (JDiagram as IJContainer<JElement>).ToList();
            for (int index = 0; index < elementList.Count; index++)
            {
                if (elementList[index].ModelId == jModel.Id)
                {
                    JDiagram.Remove(elementList[index]);
                    _elements.RemoveAt(index);
                    break;
                }
            }

            List<JRelation> needRemoveRelation = new List<JRelation>();
            List<JRelation> relationList = (JData.Current.Solution as IJContainer<JRelation>).ToList();
            foreach (var jRelation in relationList)
            {
                if (jRelation.FromId == jModel.Id || jRelation.ToId == jModel.Id)
                {
                    needRemoveRelation.Add(jRelation);
                }
            }
            if (needRemoveRelation != null)
            {
                for(int linkIndex = 0; linkIndex <_links.Count;)
                {
                    var jLink = _links[linkIndex];
                    if (needRemoveRelation.Contains(jLink.Relation))
                    {
                        _links.Remove(jLink);
                        this._ctlCanvas.Children.Remove((UIElement)jLink);
                    }
                    else
                    {
                        linkIndex++;
                    }
                }
            }
        }

        protected override void OnDrop(DragEventArgs e)
        {
            Point pos = e.GetPosition(this._ctlCanvas);
            object data = e.Data.GetData(typeof(JMT.Data.JClass));
            if (data != null)
            {
                JMT.Data.JClass jClass = data as JMT.Data.JClass;
                AddElement(jClass, pos.X, pos.Y);
            }
            else
            {
                data = e.Data.GetData(typeof(JMT.Data.JInterface));
                if (data != null)
                {
                    JMT.Data.JInterface jClass = data as JMT.Data.JInterface;
                    AddElement(jClass, pos.X, pos.Y);
                }
            }

            base.OnDrop(e);
        }

        #region 添加关联关系
        public void BeginLink(string type)
        {
            _linkType = type;
            _mode = DiagramMode.Link;
            Cursor = Cursors.Cross;
        }
        public void EndLink()
        {
            _mode = DiagramMode.Render;
            Cursor = Cursors.Arrow;
        }
        #endregion

        public Size MeasureSize()
        {
            Size size = new Size();
            foreach (var jBox in this._elements)
            {
                Rect rect = jBox.GetRect();
                if (rect.Right > size.Width)
                {
                    size.Width = rect.Right + 10;
                }
                if(rect.Bottom > size.Height)
                {
                    size.Height = rect.Bottom + 10;
                }
            }
            return size;
        }

        public void CopyToImage()
        {
            Transform transform = this._ctlCanvas.LayoutTransform;
            this._ctlCanvas.LayoutTransform = null;

            Size curSize = new Size(this._ctlCanvas.ActualWidth, this._ctlCanvas.ActualHeight);
            Size size = MeasureSize();// new Size(this._ctlCanvas.Width, this._ctlCanvas.Height);
            this._ctlCanvas.Measure(size);
            this._ctlCanvas.Arrange(new Rect(size));

            RenderTargetBitmap renderBitmap =
              new RenderTargetBitmap(
                (int)size.Width,
                (int)size.Height,
                96d,
                96d,
                PixelFormats.Pbgra32);
            renderBitmap.Render(this._ctlCanvas);

            using (Stream outStream = new MemoryStream())
            {
                BitmapImage source = new BitmapImage();
                PngBitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
                encoder.Save(outStream);
                source.BeginInit();
                source.StreamSource = outStream;
                source.EndInit();
                Clipboard.SetImage(source);
            }
            this._ctlCanvas.Measure(curSize);
            this._ctlCanvas.Arrange(new Rect(curSize));
            this._ctlCanvas.LayoutTransform = transform;
        }
    }
}
